#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/list.h>
#include "device_file.h"


struct dentry* g_Parent_Dentry;
/*Number of inodes*/
unsigned long* g_Inode_Numbers;
int g_inode_count=0;

void** g_Old_inode_Pointer;
void** g_Old_fop_Pointer;
void** g_Old_iop_Pointer;

void** g_Old_parent_inode_pointer;
void** g_Old_parent_fop_pointer;

filldir_t real_filldir;


/***********************File parent OPERATIONS*****************************/
static struct file_operations new_parent_fop =
{
.owner=THIS_MODULE,
.readdir=_readdir,
};

static int new_filldir (void *buf, const char *name, int namelen, loff_t offset,u64 ux64, unsigned ino)
{
	unsigned int i=0;
	struct dentry* pDentry;
	struct qstr Current_Name;

	Current_Name.name=name;
	Current_Name.len=namelen;
	Current_Name.hash=full_name_hash (name, namelen);

	pDentry=d_lookup(g_Parent_Dentry, &Current_Name);
	
	if (pDentry!=NULL)
	{

		for(i=0; i<=g_inode_count-1; i++)
		{
			if (g_Inode_Numbers[i]==pDentry->d_inode->i_ino) 
			{
		
			return 0;

			}
		}
	}

	return real_filldir (buf, name, namelen, offset, ux64, ino);
}

int _readdir (struct file *file, void *dirent, filldir_t filldir)
{
	g_Parent_Dentry=file->f_dentry;

	real_filldir=filldir;

	return file->f_dentry->d_parent->d_inode->i_fop->readdir(file, dirent, new_filldir);
}
/********************************FILE OPERATIONS*************************/
static struct file_operations new_fop =
{
	.owner=THIS_MODULE,
	.readdir=new_readdir,
	.release=new_release,
	.open=new_open,
	.read=new_read, 
	.write=new_write,
};

ssize_t new_read (struct file *file1, char __user * u, size_t t, loff_t *ll)
{
	printk( KERN_ALERT "Entered in new_read\n");
	return -2;
}

ssize_t new_write (struct file * file1, const char __user * u, size_t t, loff_t *ll)
{
	printk( KERN_ALERT "Entered in new_write\n");
	return -2;
}

int new_release (struct inode * new_inode, struct file *file)
{
	printk( KERN_ALERT "Entered in new_release \n");
	return 0;
}

int new_flush (struct file *file, fl_owner_t id)
{
	printk( KERN_ALERT "Entered in new_flush \n");
	return 0;
}

int new_readdir (struct file *file, void *dirent, filldir_t filldir)
{
	printk( KERN_ALERT "Entered in new_readdir \n");
	return -1;
}

int new_open (struct inode * old_inode, struct file * old_file)
{
	printk( KERN_ALERT "Entered in new_open \n");
	return -2;
}

/********************************INODE OPERATIONS*************************/
static struct inode_operations new_iop =
{
	.getattr=new_getattr,
	.rmdir=new_rmdir,
};

int new_rmdir (struct inode *new_inode,struct dentry *new_dentry)
{
	printk( KERN_ALERT "Entered in new_rmdir \n");
	return -2;
}

int new_getattr (struct vfsmount *mnt, struct dentry * new_dentry, struct kstat * ks)
{
	printk( KERN_ALERT "Entered in new_getatr \n");
	return -2;
}

/*Allocate memmory for arrays*/
void allocate_memmory()
{
        g_Old_inode_Pointer=(void*)kmalloc(sizeof(void*),GFP_KERNEL);
        g_Old_fop_Pointer=(void*)kmalloc(sizeof(void*),GFP_KERNEL);
        g_Old_iop_Pointer=(void*)kmalloc(sizeof(void*),GFP_KERNEL);

        g_Old_parent_inode_pointer=(void*)kmalloc(sizeof(void*),GFP_KERNEL);
        g_Old_parent_fop_pointer=(void*)kmalloc(sizeof(void*),GFP_KERNEL);
           
        g_Inode_Numbers=(unsigned long*)kmalloc(sizeof(unsigned long),GFP_KERNEL);

}

void reallocate_memmory()
{
	/*Realloc memmory for inode number*/
	g_Inode_Numbers=(unsigned long*)krealloc(g_Inode_Numbers,sizeof(unsigned long*)*(g_inode_count+1), GFP_KERNEL);
	
	/*Realloc memmory for old pointers*/
	g_Old_inode_Pointer=(void*)krealloc(g_Old_inode_Pointer, sizeof(void*)*(g_inode_count+1),GFP_KERNEL);
	g_Old_fop_Pointer=(void*)krealloc(g_Old_fop_Pointer, sizeof(void*)*(g_inode_count+1),GFP_KERNEL);
	g_Old_iop_Pointer=(void*)krealloc(g_Old_iop_Pointer, sizeof(void*)*(g_inode_count+1),GFP_KERNEL);

  	g_Old_parent_inode_pointer=(void*)krealloc(g_Old_parent_inode_pointer, sizeof(void*)*(g_inode_count+1),GFP_KERNEL);
  	g_Old_parent_fop_pointer=(void*)krealloc(g_Old_parent_fop_pointer, sizeof(void*)*(g_inode_count+1),GFP_KERNEL);

}



/*Function for hook functions of specified file*/
unsigned long hook_functions(const char * file_path) 
{
	int error=0;
	struct nameidata nd;

	error = path_lookup (file_path, 0, &nd);

	if(error) 
	{
		printk( KERN_ALERT "Can't access file\n");

		return -1;
	}
	
	if (g_inode_count==0)
	{
		allocate_memmory();
	}

	if (g_Inode_Numbers==NULL)
	{
			
		printk( KERN_ALERT "Not enought memmory in buffer\n");

	return -1;

	}

	/************************Old pointers**********************************/
	/*Save pointers*/
	g_Old_inode_Pointer[g_inode_count]=nd.path.dentry->d_inode;
        g_Old_fop_Pointer[g_inode_count]=(void *)nd.path.dentry->d_inode->i_fop;
        g_Old_iop_Pointer[g_inode_count]=(void *)nd.path.dentry->d_inode->i_op;

 	g_Old_parent_inode_pointer[g_inode_count]=nd.path.dentry->d_parent->d_inode;
        g_Old_parent_fop_pointer[g_inode_count]=(void *)nd.path.dentry->d_parent->d_inode->i_fop;

	/*Save inode number*/
	g_Inode_Numbers[g_inode_count]=nd.path.dentry->d_inode->i_ino;
	g_inode_count=g_inode_count+1;

	reallocate_memmory();

	/*filldir hook*/
	nd.path.dentry->d_parent->d_inode->i_fop=&new_parent_fop;

	/* Hook of commands for file*/
	nd.path.dentry->d_inode->i_op=&new_iop;
	nd.path.dentry->d_inode->i_fop=&new_fop;

	return 0;
 }

/*Function for backup of functions of the specified file*/
unsigned long backup_functions()
{	
	int i=0;
	struct inode* pInode;
	struct inode* pParentInode;
		
	for (i=0; i<g_inode_count; i++)
	{
		pInode=g_Old_inode_Pointer[(g_inode_count-1)-i];
		pInode->i_fop=(void *)g_Old_fop_Pointer[(g_inode_count-1)-i];
		pInode->i_op=(void *)g_Old_iop_Pointer[(g_inode_count-1)-i];

		pParentInode=g_Old_parent_inode_pointer[(g_inode_count-1)-i];
		pParentInode->i_fop=(void *)g_Old_parent_fop_pointer[(g_inode_count-1)-i];
		
	}

	kfree(g_Old_inode_Pointer);
	kfree(g_Old_fop_Pointer);
	kfree(g_Old_iop_Pointer);

	kfree(g_Old_parent_inode_pointer);
	kfree(g_Old_parent_fop_pointer);

	kfree(g_Inode_Numbers);

	return 0;
 }

